package com.tinem.platform.web.notice.filter;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSON;
import com.tinem.platform.module.pojo.co.GatewayHeadName;
import com.tinem.platform.module.pojo.co.RedisKeyEnum;
import com.tinem.platform.module.pojo.dto.NotifyHttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author fzh
 * @version v1
 * @program: platform
 * @className NoticeFilter
 * @description
 * @site
 * @company
 * @create 2021-11-12 16:40
 */
@Slf4j
@AllArgsConstructor
public class NoticeFilter implements Filter {

    RestTemplate restTemplate;

    StringRedisTemplate stringRedisTemplate;

    @SneakyThrows
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        String queryString = httpServletRequest.getQueryString();
        if(queryString==null){
            queryString = "";
        }
        String servletPath = httpServletRequest.getServletPath();
        servletPath = servletPath.substring(1);
        String serviceName = servletPath.substring(0,servletPath.indexOf("/"));
        String servicePath = servletPath.substring(servletPath.indexOf("/"));
        HttpMethod method = HttpMethod.valueOf(httpServletRequest.getMethod());
        log.info("request method:({}); url:({}) query:({})",method.name(),httpServletRequest.getServletPath(),httpServletRequest.getQueryString());

        Enumeration paramsNames = request.getParameterNames();
        StringBuilder requestBody= new StringBuilder();
        while (paramsNames.hasMoreElements()){
            String name	=(String) paramsNames.nextElement();
            if(name==null){
                name="";
            }
            String value = request.getParameter(name);
            if(value==null){
                value="";
            }
            log.debug("req params {}:{}",name,value);
            requestBody.append(name);
            requestBody.append("=");
            requestBody.append(value);
            requestBody.append("&");
        }
        if(requestBody.length()>0){
            requestBody.setLength(requestBody.length()-1);
        }
        NotifyHttpServletResponse resp = restTemplate.execute(new URI(StrUtil.format("http://{}/{}?{}",serviceName,servicePath,queryString))
                , method
                , new RequestCallback(){

                    @Override
                    public void doWithRequest(ClientHttpRequest r) throws IOException {
                        Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
                        while (headerNames.hasMoreElements()) {
                            String key = headerNames.nextElement();
                            String value = httpServletRequest.getHeader(key);
                            r.getHeaders().put(key, new ArrayList(){{add(value);}});
                        }
                        String uuid = UUID.randomUUID().toString();

                        Map<String,String> req = new HashMap<>(16);
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_TIMESTAMP,System.currentTimeMillis()+"");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_CONTEXT_ACCEPT_TIMESTAMP,System.currentTimeMillis()+"");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_SERVICE,serviceName);
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_METHOD,servicePath);
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_VERSION,"0.0.1-SNAPSHOT");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_API_REQUEST_ID,uuid);
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_JWT,"");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_CHARSET,"UTF-8");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_LANG,"en-us");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_SIGN_TYPE,"");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_SIGN, "");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_CRYPTO_TYPE,"");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_CRYPTO_KEY,"");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_CRYPTO_IV,"");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_DEBUG,"");
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_REQUEST_ID,uuid);
                        req.put(GatewayHeadName.X_PLATFORM_GATEWAY_CONTEXT_CLIENT_ID,"platform-cloud-web-notice");

                        String key = RedisKeyEnum.gateway_req_info.getKey(uuid);
                        stringRedisTemplate.opsForHash().putAll(key,req);

                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_TIMESTAMP,new ArrayList(){{add(System.currentTimeMillis()+"");}});
                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_SERVICE,new ArrayList(){{add(serviceName);}});
                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_METHOD,new ArrayList(){{add(servicePath);}});
                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_VERSION,new ArrayList(){{add(req.get(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_VERSION));}});
                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_API_REQUEST_ID,new ArrayList(){{add(uuid);}});
                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_JWT,new ArrayList(){{add(req.get(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_JWT));}});
                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_CHARSET,new ArrayList(){{add(req.get(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_CHARSET));}});
                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_LANG,new ArrayList(){{add(req.get(GatewayHeadName.X_PLATFORM_GATEWAY_REQ_LANG));}});
                        r.getHeaders().put(GatewayHeadName.X_PLATFORM_GATEWAY_REQUEST_ID,new ArrayList(){{add(uuid);}});

                        if(method == HttpMethod.POST){
                            IoUtil.writeUtf8(r.getBody(),false,requestBody.toString());
                            r.getBody().flush();
                            r.getBody().close();
                        }
                    }
                }, new ResponseExtractor<NotifyHttpServletResponse>(){
                    @Override
                    public NotifyHttpServletResponse extractData(ClientHttpResponse r) throws IOException {
                        String s = IoUtil.read(r.getBody(), StandardCharsets.UTF_8);
                        log.info("res:{}",s);
                        if(!r.getStatusCode().is2xxSuccessful()){
                            return new NotifyHttpServletResponse(r.getStatusCode().value(),null,s);
                        }
                        if(!"true".equals(r.getHeaders().getFirst(GatewayHeadName.X_PLATFORM_RES_SUCCESS))){
                            return new NotifyHttpServletResponse(r.getStatusCode().value(),null, Base64.decodeStr(r.getHeaders().getFirst(GatewayHeadName.X_PLATFORM_RES_MESSAGE)));
                        }
                        return JSON.parseObject(s,NotifyHttpServletResponse.class);

                    }
                });
        switch (resp.getStatus()){
            case HttpStatus.HTTP_MOVED_PERM:
            case HttpStatus.HTTP_MOVED_TEMP:
                try {
                    httpServletResponse.setStatus(resp.getStatus());
                    httpServletResponse.sendRedirect(resp.getUrl());
                } catch (IOException e) {
                    log.error("系统错误",e);
                }
                break;
            default:
                try {
                    PrintWriter writer = response.getWriter();
                    writer.println(resp.getBody());
                    writer.flush();
                    writer.close();
                } catch (IOException e) {
                    log.error("系统错误",e);
                }
                break;
        }
    }
}
